﻿using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;

namespace TPG.SharePoint.Workflow
{
    /// <summary>
    /// Provides the basic utilities required by a workflow form used
    /// to modifiy an existing workflow instance.
    /// </summary>
    /// <remarks>
    /// This class implements the required framework code for handling workflow
    /// associations. Global tasks provided by this class are
    /// <list type="bullet">
    ///     <item>Parsing and managing the Url parameters</item>
    ///     <item>Modifying the existing workflow instance</item>
    ///     <item>Management of the progress display while processing</item>
    ///     <item>Redirecting to the approriate page on completion</item>
    /// </list>
    /// 
    /// As the implementer of workflow modification forms you can
    /// derive your custom form from this class. By calling 
    /// <see cref="ModificationPage.CommitModification" /> you can modify the running
    /// workflow instance and provide updated data back to the workflow instance. The 
    /// <see cref="ModificationPage.CancelModification" /> method redirects the 
    /// user back to the appropriate page. Existing modification data can be retrieved
    /// during using the <see cref="ModificationPage.GetModificationData" /> method.
    /// </remarks>
    public abstract class ModificationPage 
        : ListWorkflowPage
    {
        #region [===== Instance fields =====]
        private SPWorkflow _workflow;
        private SPWorkflowModification _workflowModification;
        #endregion

        #region [===== Properties =====]
        /// <summary>
        /// Gets the <see cref="SPWorkflowModification" /> object representing
        /// the modification that this workflow page configures.
        /// </summary>
        protected SPWorkflowModification WorkflowModification
        {
            get
            {
                if (_workflowModification == null)
                {
                    _workflowModification = this.Workflow.Modifications[
                        QueryStringParser.GetParameter<Guid>("ModificationID")];
                }
                return _workflowModification;
            }
        }

        /// <summary>
        /// Gets the <see cref="SPWorkflow" /> object that represents
        /// the workflow that this modification form will modify.
        /// </summary>
        protected SPWorkflow Workflow
        {
            get
            {
                if (_workflow == null)
                {
                    _workflow = new SPWorkflow(
                        this.Web, 
                        QueryStringParser.GetParameter<Guid>("WorkflowInstanceID"));
                }
                return _workflow;
            }
        }
        #endregion

        #region [===== ListWorkflowPage Implementation =====]
        internal override SPWorkflowAssociation GetWorkflowAssociation()
        {
            return this.Workflow.ParentAssociation;
        }

        internal override SPWorkflowTemplate GetWorkflowTemplate()
        {
            return WorkflowAssociationInternal.BaseTemplate;
        }
        internal override WorkflowMetaData GetMetaData()
        {
            return new ModificationMetaData()
            {
                PageType = PageType.Modification,
                WorkflowName = WorkflowAssociationInternal.Name
            };
        }
        #endregion

        #region [===== Protected methods =====]
        /// <summary>
        /// Commits a workflow modification and then redirects to the page defined
        /// in the Source url parameter or the list's default view.
        /// </summary>
        /// <remarks>
        /// This method should be called for modifying all workflow instances. 
        /// Custom modification data can be passed in as a 
        /// <see cref="String" />, for instance created by serializing an 
        /// object using the <see cref="WorkflowDataSerializer" />.
        /// </remarks>
        /// <param name="data">The data to pass to the workflow instance as 
        /// modification data.</param>
        protected void CommitModification(ModificationData data)
        {
            using (SPLongOperation operation = new SPLongOperation(this))
            {
                operation.LeadingHTML = GetProgressHtml();
                operation.Begin();

                // start the new workflow instante
                this.Web.Site.WorkflowManager.ModifyWorkflow(
                    this.Workflow, this.WorkflowModification, data.NativeData);

                operation.End(this.List.DefaultViewUrl, SPRedirectFlags.UseSource, this.Context, null);
            }
        }

        /// <summary>
        /// Cancels the instantiation by redirecting to the page
        /// in the source url parameter or the list's default view.
        /// </summary>
        protected void CancelModification()
        {
            SPUtility.Redirect(this.List.DefaultViewUrl,
                SPRedirectFlags.UseSource, Context);
        }

        /// <summary>
        /// Gets the data from the current workflow modification.
        /// </summary>
        /// <returns>The context data associated with the workflow modification.</returns>
        protected ModificationData GetModificationData()
        {
            return ModificationData.CreateUsingStringData(
                this.WorkflowModification.ContextData);
        }

        /// <summary>
        /// Returns a metadata property from the workflow template.
        /// </summary>
        /// <param name="name">The name of the property to retrieve.</param>
        /// <returns>The value of the property.</returns>
        protected object GetWorkflowTemplateData(string name)
        {
            return WorkflowTemplateInternal[name];
        }

        /// <summary>
        /// Returns the progress html that is displayed for long running operations.
        /// </summary>
        /// <returns>The HTML to display with the progress image.</returns>
        protected virtual string GetProgressHtml()
        {
            return "Working..."; // TODO: use resources to load this data
        }
        #endregion
    }
}
